﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Drifting.MotionPlanning.SingleThreading.Planners;
using Drifting.Scenes;

namespace Drifting
{
    public enum ConsoleMode
    {
        ViewControl,
        TextCommand
    }
    public class Console
    {
        Camera camera;
        Game game;
        Scene scene;
        
        Planner planner;
        
        private SpriteBatch spriteBatch;
        private bool hide;
        private SpriteFont font;
        private StringBuilder sb;
        private int max_console;
        private int current_position;
        private List<String> console;
        private List<String> info;
        private string currentLine;

        private double fps = 0;
        private Vector2 CaptionPos;
        private Vector2 currentLinePos;
        private Vector2 infoPos;
        private Vector2 infoLinePos;

        private ConsoleMode mode;
        TimeSpan lastKeyCommand;

        private List<String> planner_info;

        public Console(SpriteBatch spriteBatch, SpriteFont font)
        {
            hide = true;
            this.spriteBatch = spriteBatch;
            this.font = font;
            sb = new StringBuilder();
            max_console = 12;
            current_position = 0;
            console = new List<string>();
            currentLine = "";
            for (int i = 0; i < max_console; ++i)
                console.Add(null);
            CaptionPos = new Vector2(10, 240);
            infoPos = new Vector2(10, 5);
            mode = ConsoleMode.ViewControl;
            info = new List<string>(10);
            info.Add("fps = 0");

            Trace("Console initialized");
        }

        public Game Application
        {
            get { return game; }
            set { game = value; }
        }

        public Camera View
        {
            get { return camera; }
            set { camera = value; }
        }

        public Planner Planner
        {
            get { return this.planner; }
            set { this.planner = value; }
        }

        public Scene Scene
        {
            set { this.scene = value; }
        }

        public double FPS
        {
            set { fps = value; }
        }

        public bool Hide
        {
            get { return hide; }
            set { hide = value; }
        }

        public void Draw()
        {
            spriteBatch.Begin();

            //draw info
            if (infoLinePos == null) infoLinePos = new Vector2(infoPos.X, infoPos.Y);
            infoLinePos.X = infoPos.X;
            infoLinePos.Y = infoPos.Y;

            info[0] = "fps = " + Math.Floor(fps + 0.5).ToString();

            if (planner_info == null) planner_info = new List<string>(1);
            if (planner != null)
                planner.GetStatistics(ref planner_info);

            if (scene != null)
                scene.GetStatistics(ref planner_info);



            for (int i = 0; i<planner_info.Count;++i )
            {
                int key = i+1;
                updateInfo(ref key, planner_info[i]);
            }
            
            for (int i = 0; i < info.Count; ++i)
            {
                spriteBatch.DrawString(font, info[i], infoLinePos, Color.Red);
                infoLinePos.Y = infoPos.Y + (i + 1) * 20;
            }

            if (hide == false)
            {
                sb.Clear();
                for (int i = current_position; i < console.Count; ++i)
                {
                    if (console[i] != null)
                    {
                        sb.Append(console[i]);
                        sb.Append('\n');
                    }
                }
                for (int i = 0; i < current_position; ++i)
                {
                    if (console[i] != null)
                    {
                        sb.Append(console[i]);
                        sb.Append('\n');
                    }
                }

                spriteBatch.DrawString(font, sb, CaptionPos, Color.DarkBlue);
                if (currentLine != null)
                {
                    Vector2 size = font.MeasureString(sb);
                    if (currentLinePos == null) currentLinePos = new Vector2(CaptionPos.X, CaptionPos.Y);
                    currentLinePos.Y = CaptionPos.Y + size.Y - 18;
                    spriteBatch.DrawString(font, ": " + currentLine, currentLinePos, Color.DarkBlue);
                }
            }

            spriteBatch.End();
        }

        public void Update(ref Matrix viewMatrix, ref Matrix projectionMatrix, GameTime gameTime)
        {
            TimeSpan ts = (gameTime.TotalGameTime - lastKeyCommand);

            switch (mode)
            {
                case ConsoleMode.ViewControl:

                    camera.Update(ref viewMatrix, ref projectionMatrix);

                    if (ts.TotalMilliseconds >= 100)
                    {
                        if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Enter))
                        {
                            mode = ConsoleMode.TextCommand;
                            this.hide = false;
                        }
                        else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)
                            &&   Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.LeftShift))
                        {
                            game.Exit();
                        }

                        lastKeyCommand = gameTime.TotalGameTime;
                    }
                    break;

                case ConsoleMode.TextCommand:
                    if (ts.TotalMilliseconds >= 75)
                    {
                        KeyboardState ks = Keyboard.GetState(PlayerIndex.One);
                        if (ks.IsKeyDown(Keys.Escape))
                        {
                            mode = ConsoleMode.ViewControl;
                            this.hide = true;
                            lastKeyCommand = gameTime.TotalGameTime;
                        }
                        else if (ks.IsKeyDown(Keys.Back))
                        {
                            if (currentLine.Length > 0)
                            {
                                currentLine = currentLine.Substring(0, currentLine.Length - 1);
                                lastKeyCommand = gameTime.TotalGameTime;
                            }
                        }
                        else if(ks.IsKeyDown(Keys.Enter))
                        {
                            string content = currentLine.Trim();
                            if (content.Equals("") == false)
                            {
                                Trace(content);
                                currentLine = "";
                                lastKeyCommand = gameTime.TotalGameTime;
                            }
                        }
                        else
                        {
                            // text reading
                            char pressed = '#';
                            char lastPressed = '#';

                            string translated = translateKey(ks);
                            if (translated.Length > 0)
                                pressed = translated[translated.Length - 1];
                            if (currentLine.Length>0)
                                lastPressed = currentLine[currentLine.Length - 1];

                            if (pressed != '#')
                            {
                                if (pressed != lastPressed)
                                {
                                    currentLine = currentLine + pressed.ToString().ToLower();
                                    lastKeyCommand = gameTime.TotalGameTime;
                                }
                                else if (ts.TotalMilliseconds >= 150)
                                {
                                    currentLine = currentLine + pressed.ToString().ToLower();
                                    lastKeyCommand = gameTime.TotalGameTime;
                                }
                            }
                        }
                    }
                    break;
            }
            
        }

        public void Trace(string msg)
        {
            lock (console)
            {
                console[current_position] = msg;
                current_position++;
                if (current_position >= max_console)
                    current_position = 0;
            }
        }

        string translateKey(KeyboardState ks)
        {
            string output = "";

            if (ks.IsKeyDown(Keys.D0))
            {
                output = "0";
            }
            else if (ks.IsKeyDown(Keys.D1))
            {
                output = "1";
            }
            else if (ks.IsKeyDown(Keys.D2))
            {
                output = "2";
            }
            else if (ks.IsKeyDown(Keys.D3))
            {
                output = "3";
            }
            else if (ks.IsKeyDown(Keys.D4))
            {
                output = "4";
            }
            else if (ks.IsKeyDown(Keys.D5))
            {
                output = "5";
            }
            else if (ks.IsKeyDown(Keys.D6))
            {
                output = "6";
            }
            else if (ks.IsKeyDown(Keys.D7))
            {
                output = "7";
            }
            else if (ks.IsKeyDown(Keys.D8))
            {
                output = "8";
            }
            else if (ks.IsKeyDown(Keys.D9))
            {
                output = "9";
            }
            else if (ks.IsKeyDown(Keys.Space))
            {
                output = " ";
            }
            else if (ks.IsKeyDown(Keys.A))
            {
                output = "a";
            }
            else if (ks.IsKeyDown(Keys.B))
            {
                output = "b";
            }
            else if (ks.IsKeyDown(Keys.C))
            {
                output = "c";
            }
            else if (ks.IsKeyDown(Keys.D))
            {
                output = "d";
            }
            else if (ks.IsKeyDown(Keys.E))
            {
                output = "e";
            }
            else if (ks.IsKeyDown(Keys.F))
            {
                output = "f";
            }
            else if (ks.IsKeyDown(Keys.G))
            {
                output = "g";
            }
            else if (ks.IsKeyDown(Keys.H))
            {
                output = "h";
            }
            else if (ks.IsKeyDown(Keys.I))
            {
                output = "i";
            }
            else if (ks.IsKeyDown(Keys.J))
            {
                output = "j";
            }
            else if (ks.IsKeyDown(Keys.K))
            {
                output = "k";
            }
            else if (ks.IsKeyDown(Keys.L))
            {
                output = "l";
            }
            else if (ks.IsKeyDown(Keys.M))
            {
                output = "m";
            }
            else if (ks.IsKeyDown(Keys.N))
            {
                output = "n";
            }
            else if (ks.IsKeyDown(Keys.O))
            {
                output = "o";
            }
            else if (ks.IsKeyDown(Keys.P))
            {
                output = "p";
            }
            else if (ks.IsKeyDown(Keys.Q))
            {
                output = "q";
            }
            else if (ks.IsKeyDown(Keys.R))
            {
                output = "r";
            }
            else if (ks.IsKeyDown(Keys.S))
            {
                output = "s";
            }
            else if (ks.IsKeyDown(Keys.T))
            {
                output = "t";
            }
            else if (ks.IsKeyDown(Keys.U))
            {
                output = "u";
            }
            else if (ks.IsKeyDown(Keys.V))
            {
                output = "v";
            }
            else if (ks.IsKeyDown(Keys.W))
            {
                output = "w";
            }
            else if (ks.IsKeyDown(Keys.X))
            {
                output = "x";
            }
            else if (ks.IsKeyDown(Keys.Y))
            {
                output = "y";
            }
            else if (ks.IsKeyDown(Keys.Z))
            {
                output = "z";
            }

            return output;
        }

        public bool updateInfo(ref int key, string content)
        {
            if (content.Trim().Length < 2)
                return false;

            if (key == 0)
                return false;

            if (key >= info.Count || key < 0)
            {
                // insert
                info.Add(content);
                key = info.Count - 1;
            }
            else
            {
                // update
                info[key] = content;
            }

            return true;
        }
    }
}
